#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/console.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/freezer.h>
#include <linux/spi/spi.h>
#include <linux/timer.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>

#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/irq.h>
#include <asm/io.h>

#include "wk2xxx_regs.h"

static DEFINE_MUTEX(wk2xxxs_reg_lock);

#define WK2XXX_SPI_SPEED 10000000


/*
 * This function read wk2xxx of Global register:
 */
int wk2xxx_read_global_reg(struct spi_device *spi, uint8_t reg, uint8_t *dat)
{
    struct spi_message msg;
    uint8_t buf_wdat[2];
    uint8_t buf_rdat[2];
    int status;
    struct spi_transfer index_xfer = {
        .len = 2,
        //.cs_change      = 1,
        .speed_hz = WK2XXX_SPI_SPEED,
    };
    mutex_lock(&wk2xxxs_reg_lock);
    status = 0;
    spi_message_init(&msg);
    buf_wdat[0] = 0x40 | reg;
    buf_wdat[1] = 0x00;
    buf_rdat[0] = 0x00;
    buf_rdat[1] = 0x00;
    index_xfer.tx_buf = buf_wdat;
    index_xfer.rx_buf = (void *)buf_rdat;
    spi_message_add_tail(&index_xfer, &msg);
    status = spi_sync(spi, &msg);
    mutex_unlock(&wk2xxxs_reg_lock);
    if (status)
    {
        return status;
    }
    *dat = buf_rdat[1];
    return 0;
}
/*
 * This function write wk2xxx of Global register:
 */
int wk2xxx_write_global_reg(struct spi_device *spi, uint8_t reg, uint8_t dat)
{
    struct spi_message msg;
    uint8_t buf_reg[2];
    int status;
    struct spi_transfer index_xfer = {
        .len = 2,
        //.cs_change      = 1,
        .speed_hz = WK2XXX_SPI_SPEED,
    };
    mutex_lock(&wk2xxxs_reg_lock);
    spi_message_init(&msg);
    /* register index */
    buf_reg[0] = 0x00 | reg;
    buf_reg[1] = dat;
    index_xfer.tx_buf = buf_reg;
    spi_message_add_tail(&index_xfer, &msg);
    status = spi_sync(spi, &msg);
    mutex_unlock(&wk2xxxs_reg_lock);
    return status;
}
/*
 * This function read wk2xxx of slave register:
 */
int wk2xxx_read_slave_reg(struct spi_device *spi, uint8_t port, uint8_t reg, uint8_t *dat)
{
    struct spi_message msg;
    uint8_t buf_wdat[2];
    uint8_t buf_rdat[2];
    int status;
    struct spi_transfer index_xfer = {
        .len = 2,
        //.cs_change      = 1,
        .speed_hz = WK2XXX_SPI_SPEED,
    };
    mutex_lock(&wk2xxxs_reg_lock);
    status = 0;
    spi_message_init(&msg);
    buf_wdat[0] = 0x40 | (((port - 1) << 4) | reg);
    buf_wdat[1] = 0x00;
    buf_rdat[0] = 0x00;
    buf_rdat[1] = 0x00;
    index_xfer.tx_buf = buf_wdat;
    index_xfer.rx_buf = (void *)buf_rdat;
    spi_message_add_tail(&index_xfer, &msg);
    status = spi_sync(spi, &msg);
    mutex_unlock(&wk2xxxs_reg_lock);
    if (status)
    {
        return status;
    }
    *dat = buf_rdat[1];
    return 0;
}
/*
 * This function write wk2xxx of Slave register:
 */
int wk2xxx_write_slave_reg(struct spi_device *spi, uint8_t port, uint8_t reg, uint8_t dat)
{
    struct spi_message msg;
    uint8_t buf_reg[2];
    int status;
    struct spi_transfer index_xfer = {
        .len = 2,
        //.cs_change      = 1,
        .speed_hz = WK2XXX_SPI_SPEED,
    };
    mutex_lock(&wk2xxxs_reg_lock);
    spi_message_init(&msg);
    /* register index */
    buf_reg[0] = ((port - 1) << 4) | reg;
    buf_reg[1] = dat;
    index_xfer.tx_buf = buf_reg;
    spi_message_add_tail(&index_xfer, &msg);
    status = spi_sync(spi, &msg);
    mutex_unlock(&wk2xxxs_reg_lock);
    return status;
}

#define MAX_RFCOUNT_SIZE 256

/*
 * This function read wk2xxx of fifo:
 */
int wk2xxx_read_fifo(struct spi_device *spi, uint8_t port, uint8_t fifolen, uint8_t *dat)
{
    struct spi_message msg;
    int status, i;
    uint8_t recive_fifo_data[MAX_RFCOUNT_SIZE + 1] = {0};
    uint8_t transmit_fifo_data[MAX_RFCOUNT_SIZE + 1] = {0};
    struct spi_transfer index_xfer = {
        .len = fifolen + 1,
        //.cs_change      = 1,
        .speed_hz = WK2XXX_SPI_SPEED,
    };
    if (!(fifolen > 0))
    {
        printk(KERN_ERR "%s,fifolen error!!\n", __func__);
        return 1;
    }
    mutex_lock(&wk2xxxs_reg_lock);
    spi_message_init(&msg);
    /* register index */
    transmit_fifo_data[0] = ((port - 1) << 4) | 0xc0;
    index_xfer.tx_buf = transmit_fifo_data;
    index_xfer.rx_buf = (void *)recive_fifo_data;
    spi_message_add_tail(&index_xfer, &msg);

    status = spi_sync(spi, &msg);
    udelay(1);
    for (i = 0; i < fifolen; i++)
        *(dat + i) = recive_fifo_data[i + 1];
    mutex_unlock(&wk2xxxs_reg_lock);
    return status;
}
/*
 * This function write wk2xxx of fifo:
 */
int wk2xxx_write_fifo(struct spi_device *spi, uint8_t port, uint8_t fifolen, uint8_t *dat)
{
    struct spi_message msg;
    int status, i;
    uint8_t recive_fifo_data[MAX_RFCOUNT_SIZE + 1] = {0};
    uint8_t transmit_fifo_data[MAX_RFCOUNT_SIZE + 1] = {0};
    struct spi_transfer index_xfer = {
        .len = fifolen + 1,
        //.cs_change      = 1,
        .speed_hz = WK2XXX_SPI_SPEED,
    };
    if (!(fifolen > 0))
    {
        printk(KERN_ERR "%s,fifolen error!!\n", __func__);
        return 1;
    }

    mutex_lock(&wk2xxxs_reg_lock);
    spi_message_init(&msg);
    /* register index */
    transmit_fifo_data[0] = ((port - 1) << 4) | 0x80;
    for (i = 0; i < fifolen; i++)
    {
        transmit_fifo_data[i + 1] = *(dat + i);
    }
    index_xfer.tx_buf = transmit_fifo_data;
    index_xfer.rx_buf = (void *)recive_fifo_data;
    spi_message_add_tail(&index_xfer, &msg);
    status = spi_sync(spi, &msg);
    mutex_unlock(&wk2xxxs_reg_lock);
    return status;
}

int wk2xxx_enable_rx(struct spi_device *spi, uint8_t port)
{
    uint8_t val = 0;
    wk2xxx_read_slave_reg(spi, port, WK2XXX_SIER, &val);
    val |= WK2XXX_RFTRIG_IEN | WK2XXX_RXOUT_IEN;
    wk2xxx_write_slave_reg(spi, port, WK2XXX_SIER, val);
    return 0;
}

int wk2xxx_enable_tx(struct spi_device *spi, uint8_t port)
{
    uint8_t val = 0;
    wk2xxx_read_slave_reg(spi, port, WK2XXX_SIER, &val);
    val |= WK2XXX_TFTRIG_IEN | WK2XXX_TFEMPTY_IEN;
    wk2xxx_write_slave_reg(spi, port, WK2XXX_SIER, val);
    return 0;
}

int wk2xxx_enable_tx_trig(struct spi_device *spi,uint8_t port){
    uint8_t val = 0;
    wk2xxx_read_slave_reg(spi, port, WK2XXX_SIER, &val);
    val |= WK2XXX_TFTRIG_IEN ;
    wk2xxx_write_slave_reg(spi, port, WK2XXX_SIER, val);
    return 0;
}

int wk2xxx_disable_rx(struct spi_device *spi, uint8_t port)
{
    uint8_t val = 0;
    wk2xxx_read_slave_reg(spi, port, WK2XXX_SIER, &val);
    val &= ~(WK2XXX_RFTRIG_IEN | WK2XXX_RXOUT_IEN);
    wk2xxx_write_slave_reg(spi, port, WK2XXX_SIER, val);
    return 0;
}
int wk2xxx_disable_tx(struct spi_device *spi, uint8_t port)
{
    uint8_t val = 0;
    wk2xxx_read_slave_reg(spi, port, WK2XXX_SIER, &val);
    val &= ~(WK2XXX_TFTRIG_IEN | WK2XXX_TFEMPTY_IEN);
    wk2xxx_write_slave_reg(spi, port, WK2XXX_SIER, val);
    return 0;
}

int wk2xxx_disable_tx_trig(struct spi_device *spi, uint8_t port)
{
    uint8_t val = 0;
    wk2xxx_read_slave_reg(spi, port, WK2XXX_SIER, &val);
    val &= ~(WK2XXX_TFTRIG_IEN);
    wk2xxx_write_slave_reg(spi, port, WK2XXX_SIER, val);
    return 0;
}

int wk2xxx_get_rx_fifo_used(struct spi_device *spi, uint8_t port)
{
    uint8_t fsr;
    uint8_t cnt;
    wk2xxx_read_slave_reg(spi, port, WK2XXX_FSR, &fsr);
    wk2xxx_read_slave_reg(spi, port, WK2XXX_RFCNT, &cnt);

    if (cnt == 0)
    {
        return (fsr & WK2XXX_RDAT) ? 256: 0;
    }

    return cnt;
}

int wk2xxx_get_tx_fifo_cap(struct spi_device *spi, uint8_t port)
{
    uint8_t fsr;
    uint8_t cnt;
    wk2xxx_read_slave_reg(spi, port, WK2XXX_FSR, &fsr);
    wk2xxx_read_slave_reg(spi, port, WK2XXX_TFCNT, &cnt);

    if (cnt == 0)
    {
        return (fsr & WK2XXX_TFULL) ? 0 : 256;
    }

    return 256 - cnt;
}

uint8_t wk2xxx_get_glob_int_flag(struct spi_device *spi)
{
    uint8_t val;
    wk2xxx_read_global_reg(spi, WK2XXX_GIFR, &val);
    return val;
}

uint8_t wk2xxx_get_int_flag(struct spi_device *spi, uint8_t port){
    uint8_t val = 0;
   wk2xxx_read_slave_reg(spi,port,WK2XXX_SIFR, &val);
   return val;
}

static int wk2xxx_enter_page_1(struct spi_device *spi, uint8_t port)
{
    int try_max = 5;
    uint8_t val = 0;
    do
    {
        wk2xxx_read_slave_reg(spi, port, WK2XXX_SPAGE, &val);
        if (val == WK2XXX_PAGE0)
        {
            wk2xxx_write_slave_reg(spi, port, WK2XXX_SPAGE, WK2XXX_PAGE1);
        }
        else
        {
            return 0;
        }
    } while (try_max > 0);

        return -1;
}

static int wk2xxx_eixt_page_1(struct spi_device *spi, uint8_t port)
{
    int try_max = 5;
    uint8_t val = 0;
    do
    {
        wk2xxx_read_slave_reg(spi, port, WK2XXX_SPAGE, &val);
        if (val == WK2XXX_PAGE1)
        {
            wk2xxx_write_slave_reg(spi, port, WK2XXX_SPAGE, WK2XXX_PAGE0);
        }
        else
        {
            return 0;
        }
    } while (try_max > 0); return -1;
}

struct wk2xxx_baud_set{
    uint16_t baud;
    uint8_t pres;
};

static struct wk2xxx_baud_set baud_set_tbl[]={
    {0x02,0}, //!< 230400
    {0x05,0}, //!< 115200
    {0x0b,0}, //!< 57600
    {0x17,0}, //!< 28800
    {0x24,0}, //!< 19200
    {0x2f,0}, //!< 14400
    {0x48,0}, //!< 9600
    {0x5f,0}, //!< 7200
    {0xbf,0}, //!< 3600
};


int wk2xxx_set_baudrate(struct spi_device *spi, uint port, int baudrate){
    uint8_t b0 = 0;
    uint8_t b1 = 0;
    int index = 1;

    uint16_t reg = 0;

    switch (baudrate)
    {
    case 115200:
        index = 1; 
        break;
    case 57600:
        index = 2; 
        break;
    case 28800:
        index = 3; 
        break;
    case 19200:
        index = 4; 
        break;
    case 14400:
        index = 5; 
        break;
    case 9600:
        index = 6; 
        break;
    case 7200:
        index = 7; 
        break;
    default:
        index = 2;
        break;
    }

    reg =  baud_set_tbl[index].baud;

    b0 = reg & 0xff;
    b1 = reg >>8;

    wk2xxx_enter_page_1(spi,port);
    wk2xxx_write_slave_reg(spi, port, WK2XXX_BAUD0, b0);
    wk2xxx_write_slave_reg(spi, port, WK2XXX_BAUD1, b1);
    wk2xxx_write_slave_reg(spi, port, WK2XXX_PRES, 0);
    wk2xxx_eixt_page_1(spi,port);
    return 0;
}

//int wk2xxx_get_baudrate(struct spi_device *spi, uint8_t port);


int wk2xxx_port_init(struct spi_device *spi, uint8_t port)
{
    uint8_t val = 0;
    if (port > 4 && port < 1)
    {
        return -EINVAL;
    }

    /* enable the uart */
    wk2xxx_read_global_reg(spi, WK2XXX_GENA, &val);
    val |= (1 << (port - 1));
    wk2xxx_write_global_reg(spi,  WK2XXX_GENA, val);

    /* soft reset */
    val = (1 << (port - 1));
    wk2xxx_write_global_reg(spi, WK2XXX_GRST, val);

    /* enable the uart tx and rx, no sleep */
    val = WK2XXX_TXEN | WK2XXX_RXEN;
    wk2xxx_write_slave_reg(spi, port, WK2XXX_SCR, val);

    /* configure the uart*/
    val = 0;
    wk2xxx_write_slave_reg(spi, port, WK2XXX_LCR, val);

    /* initialize fifos, 28 bytes and soft reset */
    wk2xxx_write_slave_reg(spi, port, WK2XXX_FCR, 0xff);

    /* change the fifo size to tx,128, rx 8*/
    wk2xxx_enter_page_1(spi,port);
    wk2xxx_write_slave_reg(spi, port, WK2XXX_RFTL, 128);
    wk2xxx_write_slave_reg(spi, port, WK2XXX_TFTL, 128);
    wk2xxx_eixt_page_1(spi,port);

    // enable ie
    //val = WK2XXX_TFEMPTY_IEN | WK2XXX_RXOUT_IEN | WK2XXX_RFTRIG_IEN;
     val = WK2XXX_TFEMPTY_IEN | WK2XXX_TFTRIG_IEN | WK2XXX_RXOUT_IEN |WK2XXX_RFTRIG_IEN;
    wk2xxx_write_slave_reg(spi, port, WK2XXX_SIER, val);

    // enable all the sub port interrupt
    val = 0x0F;
    wk2xxx_write_global_reg(spi, WK2XXX_GIER, val);

    return 0;
}

int wk2xxx_port_deinit(struct spi_device *spi, uint8_t port)
{
    uint8_t val = 0;
    if (port > 4 && port < 1)
    {
        return -EINVAL;
    }

    /* enable the uart */
    wk2xxx_read_global_reg(spi, WK2XXX_GENA, &val);
    val |= (1 << (port - 1));
    wk2xxx_write_global_reg(spi, WK2XXX_GENA, val);

    /* soft reset */
    val = (1 << (port - 1));
    wk2xxx_write_global_reg(spi, WK2XXX_GRST, val);
    return 0;
}

#define WK2XXX_GENA 0X00
#define WK2XXX_GRST 0X01
#define WK2XXX_GMUT 0X02
#define WK2XXX_GIER 0X10
#define WK2XXX_GIFR 0X11
// data size 4
int wk2xxx_dump_glob_regs(struct spi_device *spi,uint8_t *data){
    wk2xxx_read_global_reg(spi, WK2XXX_GENA, data++);
    wk2xxx_read_global_reg(spi, WK2XXX_GRST, data++);
    wk2xxx_read_global_reg(spi, WK2XXX_GIER, data++);
    wk2xxx_read_global_reg(spi, WK2XXX_GIFR, data++);
    return 0;
}
// data size 9
int wk2xxx_dump_channel_regs(struct spi_device *spi,uint8_t port , uint8_t *data){
    uint8_t i =0;

    for(i = 4;i<13;i++){
        wk2xxx_read_slave_reg(spi, port, i, data++);
    }
    return 0;
}